use std::str::FromStr;

use actix_web::{web, HttpResponse};
use wither::bson::oid::ObjectId;

use crate::{
    errors::cons_error,
    models::{
        blacklist::{BlacklistContent, BlacklistModelDTO},
        user::NormalUserModel,
    },
    services::black_list_service::BlacklistService,
    utils::authorize::check_user_has_blacklist_edit_prommission,
    AppError, AppResult, PaginationQuery,
};

mod ctrl_struct {
    use std::str::FromStr;

    use wither::bson::oid::ObjectId;

    use crate::{
        models::blacklist::{BlacklistContent, BlacklistContentDTO},
        services::black_list_service::AddToBlacklistSvrReq,
        AppResult,
    };

    define_request_struct!(AddToBlacklistReq {
        target_id: String,
        content: BlacklistContentDTO
    });
    impl AddToBlacklistReq {
        pub fn try_into_svr(self, user_id: ObjectId) -> AppResult<AddToBlacklistSvrReq> {
            Ok(AddToBlacklistSvrReq {
                target_id: ObjectId::from_str(&self.target_id)?,
                content: BlacklistContent::try_from(self.content)?,
                creator_id: user_id,
            })
        }
    }
    define_filter_request_struct!(SearchBlackListReq {
        content: BlacklistContentDTO
    });
    define_request_struct!(RemoveFromBlacklistReq {
        blacklist_id: String
    });
}

async fn search_blacklist(
    svr: web::Data<BlacklistService>,
    req: web::Json<ctrl_struct::SearchBlackListReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let filter = req.get_filter_as_document_use_default();
    let res = svr
        .search_blacklist(
            BlacklistContent::try_from(req.content)?,
            filter,
            query.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<BlacklistModelDTO>()))
}
async fn add_to_blacklist(
    svr: web::Data<BlacklistService>,
    req: web::Json<ctrl_struct::AddToBlacklistReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let svr_req = req.try_into_svr(user.id)?;
    if !check_user_has_blacklist_edit_prommission(&user, &svr_req.content) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.add_to_blacklist(svr_req).await?;

    Ok(HttpResponse::Ok().json(BlacklistModelDTO::from(res)))
}
async fn remove_from_blacklist(
    svr: web::Data<BlacklistService>,
    req: web::Json<ctrl_struct::RemoveFromBlacklistReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let blacklist_id = ObjectId::from_str(&req.blacklist_id)?;
    let blacklist = svr
        .find_one_blacklist(blacklist_id, None)
        .await?
        .ok_or_else(|| AppError::not_found(cons_error::MSG_BLACKLIST_IS_NOT_FOUND))?;
    if !check_user_has_blacklist_edit_prommission(user, blacklist.content) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.remove_from_blacklist(blacklist_id).await?;
    Ok(HttpResponse::Ok().json(BlacklistModelDTO::from(res)))
}

pub fn configure_blacklist_controller(cfg: &mut web::ServiceConfig) {
    cfg.route("/search", web::post().to(search_blacklist))
        .route("/add", web::post().to(add_to_blacklist))
        .route("/remove", web::post().to(remove_from_blacklist));
}
